{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 信号量介绍"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "信号量的特点与用途\n",
    "\n",
    "- 线程通信机制：信号量(semaphore)用于线程间通信，通过资源计数控制线程执行顺序\n",
    "- 轻量级控制：相比条件变量更简单，适用于简单的前后执行顺序控制场景\n",
    "- 系统支持：Linux系统原生支持信号量，C++20开始提供标准库支持"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Linux系统与信号量\n",
    "\n",
    "- 系统级支持：Linux系统(pthread)内置信号量实现\n",
    "- 语言演进：C++11仅提供条件变量，C++20才加入信号量支持\n",
    "- 替代方案：可用条件变量封装实现信号量功能"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "信号量的理解\n",
    "\n",
    "- 资源计数器：信号量本质是资源计数器，记录可用资源数量\n",
    "- 无上限计数：资源计数可以是任意非负整数（0,1,2,...）\n",
    "- 核心操作：\n",
    "  - wait()：资源计数减1，若无资源则阻塞\n",
    "  - post()：资源计数加1，释放资源"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "信号量与互斥锁的区别\n",
    "\n",
    "- 计数范围：\n",
    "  - 互斥锁：只能是0或1（锁定/未锁定）\n",
    "  - 信号量：可以是任意非负整数\n",
    "- 用途差异：\n",
    "  - 互斥锁：保证线程互斥访问临界区\n",
    "  - 信号量：实现线程间通信协调\n",
    "- 操作类比：\n",
    "  - lock() ≈ wait()（资源计数1→0）\n",
    "  - unlock() ≈ post()（资源计数0→1）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "信号量的定义与初始资源计数\n",
    "\n",
    "- 初始化语法：semaphore sem(初始值)\n",
    "- 初始值含义：\n",
    "  - 0：初始无可用资源\n",
    "  - N：初始有N个可用资源\n",
    "- 应用场景：\n",
    "  - 0：典型生产者-消费者模型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 信号量应用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "信号量应用于生产者消费者问题\n",
    "\n",
    "- 基本流程：\n",
    "  - 生产者：生产商品→放入容器→sem.post()\n",
    "  - 消费者：sem.wait()→取出商品→消费\n",
    "- 代码实现：\n",
    "- 控制级别：\n",
    "  - 保证\"先生产后消费\"的基本顺序\n",
    "  - 无法实现精细的同步控制（如容器满/空判断）\n",
    "- 特殊形式：semaphore sem(1)（二元信号量）\n",
    "- 行为特征：\n",
    "  - 资源计数只能在0和1之间变化\n",
    "  - 功能类似互斥锁但语义不同\n",
    "- 使用场景：简单互斥控制或线程顺序控制"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 二元信号量"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "基本概念: 二元信号量(semaphore sem(1))是一种特殊的信号量，其初始资源计数为1，可以实现与互斥锁(mutex)相似的线程互斥操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "二元信号量与互斥锁的比较\n",
    "\n",
    "- 相似性:\n",
    "  - 都能保证临界区代码段的原子操作\n",
    "  - 在多线程环境下防止竞态条件\n",
    "  - 初始状态下只允许一个线程进入临界区\n",
    "- 实现方式:\n",
    "  - 通过`sem.wait()`获取资源(计数从1减到0)\n",
    "  - 通过`sem.post()`释放资源(计数从0加到1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "二元信号量的特点\n",
    "\n",
    "- 线程准入机制:\n",
    "  - 当资源计数为`1`时，一个线程可以成功`wait`\n",
    "  - 其他线程的wait操作将被阻塞\n",
    "  - 执行完临界区代码后调用`post`释放资源\n",
    "- 资源管理:\n",
    "  - 保证临界区代码段每次只有一个线程进入\n",
    "  - 类似\"放行\"机制，有序控制线程访问"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "二元信号量与互斥锁的区别\n",
    "\n",
    "- 锁的所有权:\n",
    "  - 互斥锁必须由获取锁的线程释放锁\n",
    "  - 信号量的`wait`和`post`可以在不同线程调用\n",
    "- 操作限制:\n",
    "  - 互斥锁未获取时调用`unlock`是非法操作\n",
    "  - 信号量允许任意线程调用`post`增加资源\n",
    "- 设计目的:\n",
    "  - 互斥锁专为线程互斥设计\n",
    "  - 信号量本质是线程通信机制"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "二元信号量使用中的风险\n",
    "\n",
    "- 意外释放风险:\n",
    "  - 其他线程可能意外调用`post`\n",
    "  - 导致临界区被多个线程同时进入\n",
    "- 使用建议:\n",
    "  - 需要严格互斥时应优先使用互斥锁\n",
    "  - 仅在明确需要跨线程通信时使用信号量\n",
    "- 典型错误:\n",
    "  - 线程1执行临界区时，线程2调用`post`\n",
    "  - 导致线程3被提前放入临界区"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 信号量内容小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 核心区别:\n",
    "  - 互斥锁用于线程同步中的互斥\n",
    "  - 信号量用于线程同步中的通信\n",
    "- 实践建议:\n",
    "  - 通过实际项目(如线程池、聊天服务器)加深理解\n",
    "  - 理论需与实践结合才能真正掌握\n",
    "- 特殊形式:\n",
    "  - 二元信号量是信号量的特殊形式\n",
    "  - 虽可模拟互斥锁但存在使用风险"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PV操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- P操作本质: 对应信号量的`wait`操作，当资源不可获取时会阻塞线程\n",
    "- V操作本质: 对应信号量的`post`操作，使信号量资源计数加`1`，唤醒等待线程\n",
    "- 线程状态转换: 被唤醒的线程从阻塞态变为就绪态，等待内核调度执行"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "信号量特性:\n",
    "\n",
    "- 可视为无限制的资源计数器（不同于`mutex`的`0/1`限制）\n",
    "- `sem.wait()`和`sem.post()`可在不同线程中调用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "二元信号量:\n",
    "\n",
    "- 资源计数限制为0或1（`semaphore sem(1)`）\n",
    "- 可实现与`mutex`相同的互斥操作\n",
    "- 但锁的获取和释放可由不同线程完成"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| **对比维度**      | **PV 操作**                                   | **二元信号量**                   |\n",
    "| ----------------- | --------------------------------------------- | -------------------------------- |\n",
    "| **计数范围**      | 任意非负整数                                  | 只能是 `0` 或 `1`                |\n",
    "| **典型场景**      | 多资源管理、多线程任务分配、生产者-消费者模型 | 临界区保护、线程互斥、事件通知   |\n",
    "| **实现复杂度**    | 稍高，通常需要多个信号量配合使用              | 简单，天然适用于互斥和单资源管理 |\n",
    "| **线程同步能力**  | 强，支持复杂的线程协作                        | 弱，只适用于简单的线程同步       |\n",
    "| **RAII 封装支持** | 支持，封装计数信号量即可                      | 支持，封装二元信号量即可         |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "与条件变量的区别:\n",
    "\n",
    "- `cond.wait`类似`P`操作，`cond.notify`类似`V`操作\n",
    "- 但notify不能直接将线程从等待态变为就绪态\n",
    "- 需先转为阻塞态，待锁释放后才能竞争锁进入就绪态"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 内容总结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 关键组件:\n",
    "  - mutex互斥锁：保护共享资源，计数﻿$1\\rightarrow0$﻿表示上锁\n",
    "  - condition_variable：线程间通信机制\n",
    "  - unique_lock：自动管理锁的生命周期\n",
    "- 典型流程:\n",
    "  - 生产者：获取锁→生产商品→`notify_all`→释放锁\n",
    "  - 消费者：获取锁→检查条件→`wait`释放锁等待→被唤醒后重新获取锁\n",
    "- 注意事项:\n",
    "  - 条件检查必须使用while循环避免虚假唤醒\n",
    "  - `notify_all`比`notify_one`更安全但性能开销更大"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
   "name": "xcpp17"
  },
  "language_info": {
   "name": "C++17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
